#pragma once
#include "list"
#include <mutex>
#include "room.hpp"
#include "MYSQL_api.hpp"

using namespace std;

template <class T>
class match_queue
{
public:
    // 获取队列元素个数
    int Size()
    {
        unique_lock<mutex> lock(_mutex);
        return _list.size();
    }
    // 阻塞线程
    void wait()
    {
          unique_lock<mutex> lock(_mutex);
         _count.wait(lock);  //阻塞线程
    }
    //入队数据并且唤醒线程
    void push(const T& data)
    {
          unique_lock<mutex> lock(_mutex);
        _list.push_back(data);
        _count.notify_all();  //唤醒线程
    }
    //出队数据
    bool pop(T& data)
    {
        unique_lock<mutex> lock(_mutex);
        if(_list.empty())
        {
            return false;
        }
        data = _list.front();
        _list.pop_front();
        return true;
    }

    //删除指定用户
    void remove(T& data)
    {
        unique_lock<mutex> lock(_mutex);
         _list.remove(data);
    }
private:
    list<T> _list;                  // 该队列中用链表作为数据类型
    std::mutex _mutex;              //
    std::condition_variable _count; // 阻塞消费者，在匹配人数<2的时候就对消费者进行阻塞
};

//管理匹配队列类
class matcher
{
private:

//线程执行的任务
void handle_match(match_queue<int>& mq)
{
    while(1)
    {
    //1、队列人数是否大于2，若大于2则出队，小于2进行阻塞等待
    while(mq.Size() < 2)
    {
        mq.wait();
    }
    int uid1, uid2;
    if(!mq.pop(uid1))
    {
        continue;
    }
    if(!mq.pop(uid2))
    {
        continue;
    }
    //2、进行验证，两个玩家是否都在线，如果有人掉线则将其退出队列，并将另一个在线玩家重新加入队列
    wsserver_t::connection_ptr it1, it2;
    _om->get_conn_from_game_id(uid1, it1);
    if(it1.get() == nullptr)
    {
        mq.push(uid2);
        continue;
    }
    _om->get_conn_from_game_id(uid2, it2);
    if(it2.get() == nullptr)
    {
        mq.push(uid1);
        continue;
    }
    //3、为两个玩家创建房间并且添加他们
    room_ptr rp = _rm->create_room(uid1, uid2);
    if(rp.get() == nullptr)
    {
        mq.push(uid1);
        mq.push(uid2);
        continue;
    }
    //4、对两个玩家进行响应（返回数据帧）
    Json::Value text;
    string body;
    text["optype"] = "match_success";
    text["result"] = true;
    Json_api::serialize(text, body);
    it1->send(body);
    it2->send(body);
    }
}

//线程任务函数
void th_normal_entry(){return handle_match(_q_normal);}
void th_high_entry(){return handle_match(_q_high);}
void th_super_entry(){return handle_match(_q_super);}

match_queue<int> _q_normal; //普通玩家队列 score < 2000
match_queue<int> _q_high; //高手玩家队列  2000 < score < 3000
match_queue<int> _q_super; //超级玩家队列 score > 3000
//一个线程处理一个匹配队列
thread _th_normal; //普通玩家线程
thread _th_high;  //高手玩家线程
thread _th_super; //超级玩家线程
room_manager* _rm; //房间管理句柄
Mysql_api* _Ma; //对用户信息数据进行处理句柄
online_manger* _om; //对用户连接在线管理的句柄

public:
    matcher(room_manager* rm, Mysql_api* Ma, online_manger* om)
    :_rm(rm), _Ma(Ma), _om(om)
    ,_th_normal(std::thread(&matcher::th_normal_entry, this))
   ,_th_high(std::thread(&matcher::th_high_entry, this))
   ,_th_super(std::thread(&matcher::th_super_entry, this))
    {}

    //向匹配队列中添加用户
    bool add(int uid)
    {
        Json::Value info;
        //查询该用户的分数
        if(!_Ma->Select_id(uid, info))
        {
            DLOG("获取玩家: %d 信息失败！！！",uid);
            return false;
        }
        int score = info["score"].asInt();
        if(score <= 2000)
        {
            _q_normal.push(uid);
        } else if(score <= 3000)
        {
            _q_high.push(uid);
        }else 
        {
            _q_super.push(uid);
        }
        return true;
    }

    //从匹配队列中删除指定的用户
    bool sub(int uid)
    {
         Json::Value info;
        //查询该用户的分数
        if(!_Ma->Select_id(uid, info))
        {
            DLOG("获取玩家: %d 信息失败！！！",uid);
            return false;
        }
        int score = info["score"].asInt();
        if(score <= 2000)
        {
            _q_normal.remove(uid);
        } else if(score <= 3000)
        {
            _q_high.remove(uid);
        }else 
        {
            _q_super.remove(uid);
        }
        return true;
    }
};